package main

import (
	"flag"
	"github.com/russross/blackfriday"
	"html/template"
	"io/ioutil"
	"mime"
	"net/http"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"
	"time"
)

type mdServer struct{}

func (server *mdServer) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	defer func() {
		err := recover()
		if err == nil {
			return
		}
		writer.WriteHeader(500)
		errM, ok := err.(error)
		if ok {
			writer.Write([]byte(errM.Error()))
		}
	}()
	p := request.URL.Path
	if strings.HasPrefix(p, "/mdserver-resources/static/") {
		handleStatic(writer, request)
		return
	}
	if p == "/"+exeName {
		writer.WriteHeader(404)
		return
	}
	if p == "/mdserver-resources/api/file-date" {
		handleApi(writer, request)
		return
	}
	ext := path.Ext(p)
	if len(ext) == 0 {
		if !strings.HasSuffix(p, "/") {
			p = p + "/"
		}
		p = p + "index.md"
	} else if strings.ToLower(ext) != ".md" {
		http.ServeFile(writer, request, "."+p)
		return
	}
	if !strings.HasSuffix(p, ".md") {
		http.ServeFile(writer, request, "."+p)
	}
	handleMarkdown(writer, request, p)
}

func handleStatic(writer http.ResponseWriter, request *http.Request) {
	resName := strings.TrimLeft(request.URL.Path, "/")
	res, err := Asset(resName)
	if err != nil {
		writer.WriteHeader(404)
		writer.Write([]byte("resource not found:" + request.URL.Path))
		return
	}
	reqModiDate := request.Header.Get("If-Modified-Since")
	info, _ := AssetInfo(resName)
	modiDate := info.ModTime().Format(time.RFC1123)
	if modiDate == reqModiDate {
		writer.WriteHeader(304)
		return
	}
	if len(reqModiDate) > 0 {
	}
	ext := path.Ext(resName)
	mimeType := mime.TypeByExtension(ext)
	if len(mimeType) == 0 {
		mimeType = "application/octet-stream"
	}
	writer.Header().Set("Cache-Control", "max-age=31536000, public")
	writer.Header().Set("Content-Type", mimeType)
	now := time.Now()
	writer.Header().Set("Date", now.Format(time.RFC1123))
	writer.Header().Set("Expires", now.Add(time.Hour*24*180).Format(time.RFC1123))
	writer.Header().Set("Last-Modified", modiDate)
	writer.Write(res)
}

func handleApi(writer http.ResponseWriter, request *http.Request) {
	var t time.Time
	f := request.URL.Query().Get("file")
	if len(f) > 0 {
		stat, err := os.Stat(f)
		if err == nil {
			t = stat.ModTime()
		}
	}
	writer.Write([]byte(t.Format(time.RFC1123)))
}

func handleMarkdown(writer http.ResponseWriter, request *http.Request, p string) {
	fileStr, err := ioutil.ReadFile("." + p)
	if err != nil {
		writer.WriteHeader(500)
		writer.Write([]byte(err.Error()))
		return
	}
	tplHtml, err := Asset("mdserver-resources/tpl.gohtml")
	if err != nil {
		writer.WriteHeader(500)
		writer.Write([]byte(err.Error()))
		return
	}
	html := blackfriday.MarkdownCommon(fileStr)
	tpl, _ := template.New("tpl").Parse(string(tplHtml))
	if tpl != nil {
		var data = map[string]interface{}{
			"title": p,
			"md":    template.HTML(html),
		}
		if dev {
			stat, _ := os.Stat("." + p)
			data["dev"] = true
			data["p"] = "." + p
			data["date"] = stat.ModTime().Format(time.RFC1123)
		}
		tpl.Execute(writer, data)
	}
}

var dev bool
var addr string
var exeName string

func main() {
	flag.BoolVar(&dev, "dev", true, "run the mdserver as development mode.")
	flag.StringVar(&addr, "addr", ":7777", "")
	flag.Parse()
	args0 := strings.Replace(os.Args[0], "\\", "/", -1)
	exeName = args0[strings.LastIndex(args0, "/")+1:]
	mime.AddExtensionType(".js", "application/javascript")
	mime.AddExtensionType(".css", "text/css")
	var address = addr
	if strings.HasPrefix(address, ":") {
		address = "localhost" + address
	}
	address = "http://" + address
	println("server is running on " + address)
	if runtime.GOOS == "windows" {
		exec.Command("explorer", address).Start()
	}
	err := http.ListenAndServe(addr, &mdServer{})
	if err != nil {
		panic(err)
	}
}
